﻿namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    private static int _defaultOrder;

    private static List<ITModule> _moduleTypes = new(0);

    /// <summary>
    /// 注册模块，自动注入。
    /// </summary>
    /// <param name="services"></param>
    /// <typeparam name="TModule"></typeparam>
    /// <returns></returns>
    public static async Task<IServiceCollection> AddModule<TModule>(this IServiceCollection services)
        where TModule : ITModule
    {
        await services.AddModule<ITModule>(null);
        return services;
    }

    /// <summary>
    /// 注册模块，自动注入。
    /// </summary>
    /// <param name="services"></param>
    /// <param name="options"></param>
    /// <typeparam name="TTokenModule"></typeparam>
    /// <returns></returns>
    public static async Task<IServiceCollection> AddModule<TTokenModule>(this IServiceCollection services,
        Action<TokenModuleOptions>? options) where TTokenModule : ITModule
    {
        _defaultOrder = 1;
        var tokenModuleOptions = new TokenModuleOptions();
        options?.Invoke(tokenModuleOptions);

        var types = new List<Tuple<ITModule, int>>();
        var type = typeof(TTokenModule);
        GetModuleType(type, types, 0);

        var modules = types.OrderBy(x => x.Item2).Select(x => x.Item1).Distinct();

        using var serviceConfigurationOptions = new ServiceConfigurationOptions(services);

        // 模块初始化
        foreach (var module in modules)
        {
            await module.InitializeAsync(serviceConfigurationOptions);
            module.Initialize(serviceConfigurationOptions);
        }

        if (tokenModuleOptions.AutomaticInjection)
        {
            // 启动主动注入
            AutomaticInjection.InjectionService(services, modules.Select(x => x.GetType()));
        }

        if (modules?.Any() == true)
            _moduleTypes.AddRange(modules!.Select(x => x));

        foreach (var module in modules)
        {
            await module.ConfigureServicesAsync(serviceConfigurationOptions);
            module.ConfigureServices(serviceConfigurationOptions);
        }

        return services;
    }

    /// <summary>
    /// 初始化Application
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public static IApplicationBuilder InitializeApplication(this IApplicationBuilder app)
    {
        var types = _moduleTypes;

        var modules = types
            .Where(x => x is IApplication)?
            .Select(x => x as IApplication).Distinct();

        using var applicationShutdownOptions = new ApplicationShutdownOptions(app.ApplicationServices);

        // 模块初始化。
        foreach (var module in modules)
        {
            module.OnApplicationAsync(applicationShutdownOptions);
            module.OnApplication(applicationShutdownOptions);
        }

        // 模块初始化结束。
        foreach (var module in modules)
        {
            module.Shutdown(applicationShutdownOptions);
            module.ShutdownAsync(applicationShutdownOptions);
        }

        return app;
    }

    private static void GetModuleType(Type type, List<Tuple<ITModule, int>> types, int size)
    {
        if (!type.IsAssignableFrom<ITModule>())
        {
            return;
        }

        var typeInstance = type.Assembly.CreateInstance(type.FullName, true) as ITModule;

        if (typeInstance == null)
        {
            return;
        }

        // 叠加size，用于判断当前模块的依赖层次。
        size++;

        if (size > 30)
            throw new Exception("模块依赖层次过深，请检查模块依赖关系。");

        types.Add(new Tuple<ITModule, int>(typeInstance, GetRunOrder(type)));


        var attributes = type.GetCustomAttributes().OfType<DependOnsAttribute>()
            .SelectMany(x => x.Type).Where(x => x.IsAssignableFrom<ITModule>());

        foreach (var t in attributes)
        {
            // 创建ITokenModule实例
            ITModule? module = t.Assembly.CreateInstance(t?.FullName, true) as ITModule;
            if (module == null)
                continue;

            GetModuleType(t, types, size);
        }
    }

    private static int GetRunOrder(Type type)
    {
        var runOrder = type.GetCustomAttribute<RunOrderAttribute>();

        return runOrder?.Order ?? _defaultOrder++;
    }
}